# -*- encoding: utf-8 -*-
"""
@File    : wrong_singleton.py
@Author  : lilong
@Time    : 2021/12/18 5:44 下午
"""

"""
链接：https://www.jianshu.com/p/6a1690f0dd00
"""

import time
import threading


class Singleton(object):
    def __init__(self, *args, **kwargs):
        time.sleep(1)
        pass

    @classmethod
    def get_instance(cls, *args, **kwargs):
        if not hasattr(Singleton, '_instance'):
            Singleton._instance = Singleton(*args, **kwargs)

        return Singleton._instance


def task(arg):
    obj = Singleton.get_instance(arg)
    print(obj)


# 注意,这样的单例模式在单线程下是安全的,但是如果遇到多线程,就会出现问题.
# 如果遇到多个线程同时创建这个类的实例的时候就会出现问题.
for i in range(10):
    t = threading.Thread(target=task, args=[i, ])
    t.start()

"""
可以看到是创建了10个不同的实例对象,这是什么原因呢.因为在一个对象创建的过程中,另外一个对象也创建了.
当它判断的时候,会先去获取_instance属性,因为这个时候还没有,它就会调用init()方法.结果就是调用了10次,然后就创建了10个对象.

如何解决呢?
加锁: 在哪里加锁呢?在获取对象属性_instance的时候加锁,如果已经有人在获取对象了,其他的人如果要获取这个对象,就要等一哈.
因为前面的那个人,可能在第一次创建对象.
"""

import time
import threading


class Singleton(object):

    # 使用线程锁
    _instance_lock = threading.Lock()

    def __init__(self, *args, **kwargs):
        time.sleep(1)

    @classmethod
    def get_instance(cls, *args, **kwargs):
        if not hasattr(Singleton, '_instance'):
            with Singleton._instance_lock:
                if not hasattr(Singleton, '_instance'):
                    Singleton._instance = Singleton(*args, **kwargs)

        return Singleton._instance


def task(arg):
    obj = Singleton.get_instance(arg)
    print(obj)


for i in range(10):
    t = threading.Thread(target=task, args=[i, ])
    t.start()

obj = Singleton.get_instance()
print(obj)

"""
这种方式创建的单例,必须使用Singleton.get_instance()方法,如果使用Singleton()的话,得到的并不是单例.
所以我们推荐使用__new__()方法来创建单例,这样创建的单例可以使用类名()的方法进行实例化对象

"""